/* Copyright (C) 2020, 2019 Girish M
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301, USA.
 * 
 */
const Cache = require('./cache.model.js');

const max_cached_items = 10;

function randomData(length) {
    let result = '';
    let characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    let charactersLength = characters.length;
    for (let i = 0; i < length; i++) {
        result += characters.charAt(Math.floor(Math.random() * charactersLength));
    }
    return result;
}

function createCacheEntry(req, res) {
    Cache.count({}, (err, count) => {
        if (!err) {
            console.log("Keys in cache: ", count);
            cacheKey = req.body.key || req.params.key;
            if (count <= max_cached_items) {
                if (cacheKey) {
                    Cache.findOne({ key: cacheKey }).then(cache => {
                        if (!cache) {
                            console.log("Key not found in cache. Creating new entry.");

                            const cache = new Cache({
                                key: cacheKey,
                                data: randomData((Math.random() * 10) + 1),     //random data with random length between 1 and 10
                                ttl: Math.floor((Math.random() * 100) + 1)      //ttl is a random number between 1 and 100 minutes. 
                            });

                            cache.save().then(data => {
                                console.log("Created new cache entry ", data);
                                res.send(cache.data);
                            }).catch(err => {
                                return res.status(500).send({
                                    message: err.message || "Something wrong while creating cache entry."
                                })
                            })
                        }
                        else {
                            console.log("Updating cache entry for key: " + cacheKey);
                            return res.send(cache);
                        }
                    }).catch(err => {
                        if (err.kind === 'ObjectId') {
                            return res.status(404).send({
                                message: "Cache entry not found with key " + cacheKey
                            });
                        }
                        return res.status(500).send({
                            message: "Something wrong updating cache with key " + cacheKey
                        });
                    });
                }
            }
            else {
                //replace the cache item with shortest ttl
                console.log("Replacing the oldest cached item");
                const replacementCache = {
                    key: cacheKey,
                    data: randomData((Math.random() * 10) + 1),     //random data with random length between 1 and 10
                    ttl: Math.floor((Math.random() * 100) + 1),     //ttl is a random number between 1 and 100 minutes. 
                    createdAt: new Date().toISOString(),
                    updatedAt: new Date().toISOString()
                };
                Cache.findOneAndReplace({"ttl":{$gt : 0}}, replacementCache, {sort: {"updatedAt": 1}}).then(data => {
                    console.log("Created replacement entry ", data);
                    res.send(replacementCache.data);
                }).catch(err => {
                    if (err.kind === 'ObjectId') {
                        return res.status(404).send({
                            message: "Cache entry not found with key " + cacheKey
                        });
                    }
                    return res.status(500).send({
                        message: "Something wrong updating cache with key " + cacheKey
                    });
                });
            }
        }
        else{
            console.log(err);
            res.status(500).send({
                message: "Some error occurred while getting key count"
            })
        }
    });


}

exports.create = (req, res) => {
    if (!req.body) {
        return res.status(400).send({
            message: "Cache content cannot be empty"
        });
    }

    if (req.body.key) {
        return createCacheEntry(req, res);
    }
    else {
        return res.status(400).send({
            message: "Key cannot be empty"
        });
    }
}

exports.findAll = (req, res) => {
    Cache.find().then(cache => {
        res.send(cache);
    }).catch(err => {
        res.status(500).send({
            message: err.message || "Something wrong while creating cache entry."
        })
    })
}

exports.findOne = (req, res) => {
    Cache.findOne({ key: req.params.key })
        .then(cache => {
            if (!cache) {
                console.log("Cache miss");
                createCacheEntry(req, res);
            }
            else {
                console.log("Cache hit");
                res.send(cache.data);
            }
        }).catch(err => {
            if (err.kind === 'ObjectId') {
                return res.status(404).send({
                    message: "Cache not found with id " + req.params.key
                });
            }
            return res.status(500).send({
                message: "Something wrong retrieving cache with id " + req.params.key
            });
        });
};

exports.delete = (req, res) => {
    Cache.findOneAndRemove({ key: req.params.key })
        .then(cache => {
            if (!cache) {
                return res.status(404).send({
                    message: "Cache miss, key: " + req.params.key + " not found."
                });
            }
            return res.send({ message: "Key removed from cache!" });
        }).catch(err => {
            if (err.kind === 'ObjectId' || err.name === 'NotFound') {
                return res.status(404).send({
                    message: "Key not found with id " + req.params.key
                });
            }
            return res.status(500).send({
                message: "Could not delete key with id " + req.params.key
            });
        });
}

exports.deleteAll = (req, res) => {
    Cache.deleteMany().then(result => {
        if (!result) {
            return res.status(404).send({
                message: "Couldn't delete all keys."
            });
        }
        return res.send({ message: "All keys removed from the cache." });
    }).catch(err => {
        return res.status(500).send({
            message: "Error occured while deleting keys: " + err
        })
    })
}
